﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.SharePoint.Administration;
using System.Collections.ObjectModel;

namespace SPALM
{
    internal class AddAndDeploySolutionsOperation : DeploymentOperation
    {
        private string Solutions;
        private string WebApplications;
        private bool AllContentUrls;
        private bool Force;
        private bool AllowGACDeployment;
        private bool AllowCASPolicies;
        private bool Overwrite;     

        private string[] solutionNames;
        private string[] solutionFileNames;
        private string[] webApplicationNames;

        public AddAndDeploySolutionsOperation(Deployment parentTask, string Solutions, string WebApplications, bool ParsedAllContentUrls, bool ParsedForce, bool ParsedAllowGACDeployment, bool ParsedAllowCASPolicies, bool ParsedOverwrite, int ParsedRetries, int ParsedWaitSecondsAfterFinish, bool ParsedSimulateDeployment)
        : base(parentTask, ParsedRetries, ParsedWaitSecondsAfterFinish, ParsedSimulateDeployment)
        {            
            // TODO: Complete member initialization
            this.Solutions = Solutions;
            this.WebApplications = WebApplications;
            this.AllContentUrls = ParsedAllContentUrls;
            this.Force = ParsedForce;
            this.AllowGACDeployment = ParsedAllowGACDeployment;
            this.AllowCASPolicies = ParsedAllowCASPolicies;
            this.Overwrite = ParsedOverwrite;
            
            //Parameter extraction
            solutionNames = GetSolutionNames(Solutions);
            solutionFileNames = GetArrayFromString(Solutions);
            webApplicationNames = GetArrayFromString(WebApplications);
        }

        public override void CheckPreconditions()
        {
            LogMessage(FormatHeader("Check preconditions"), LogLevel.High);
            
            //1. Checks
            if(solutionFileNames.Length == 0)            
            {
                exceptions.Add("No solutions given in parameter 'Solutions'");
            }

            //a. Existieren alle übergeben Dateien
            LogMessage("Check existence of wsp files...", LogLevel.Normal);  
            foreach (string solutionFileName in solutionFileNames)
            {
                LogMessage("Checking solution '" + solutionFileName + "'", LogLevel.Low);
                if (!File.Exists(solutionFileName))
                {
                    exceptions.Add("Solution '" + solutionFileName + "' not found.");
                }
            }

            //Existieren die angegebenen Webapps
            if (webApplicationNames.Length > 0)
            {
                LogMessage("Check existing web applications...", LogLevel.Normal);
                foreach (string webAppName in webApplicationNames)
                {
                    LogMessage("Checking web application '" + webAppName + "'", LogLevel.Low);
                    if (!WebApplicationExistsOnServer(webAppName))
                    {
                        exceptions.Add("Web application '" + webAppName + "' does not exists on server.");
                    }
                }
            }


            //b. Existieren die Lösungen bereits auf dem Server
            LogMessage("Check for existing solutions in farm...", LogLevel.Normal);  
            foreach (string solutionName in solutionNames)
            {   LogMessage("Checking '" + solutionName + "'", LogLevel.Low);
                if (SolutionExistsOnServer(solutionName) && !Overwrite)
                {
                    exceptions.Add("Solution '" + solutionName + "' already exists on server but task parameter 'Overwrite' is not true.");
                }
            }

            LogMessage(FormatHeader("Checking preconditions finished successfully"), LogLevel.High);
            LogMessage("", LogLevel.High); 
        }

        public override void PrepareSystem()
        {
            //if a solution exists then remove the solution from the server
            string existingSolutions = "";
            foreach (string solutionName in solutionNames)
            {
                if (SolutionExistsOnServer(solutionName) && Overwrite)
                {
                    if(!string.IsNullOrEmpty(existingSolutions))
                    {
                        existingSolutions += ";";
                    }
                    existingSolutions += solutionName;
                }
            }

            if(!string.IsNullOrEmpty(existingSolutions))
            {                
                LogMessage(FormatHeader("Deleting existing solutions because parameter Overwrite is true"), LogLevel.High);
                LogMessage("", LogLevel.High); 
                new RetractAndDeleteSolutionsOperation(this.parentTask, existingSolutions, "", this.ParsedRetries, this.ParsedWaitSecondsAfterFinish, this.ParsedSimulateDeployment).Run();
            }
        }

        public override void RunOperation()
        {
            LogMessage(FormatHeader("Adding solutions to farm"), LogLevel.High);
            foreach (string solutionPath in solutionFileNames)
            {
                try
                {
                    LogMessage("Adding '" + solutionPath + "'...", LogLevel.Normal);
                    SPFarm.Local.Solutions.Add(solutionPath);
                }
                catch (Exception ex)
                {
                    exceptions.Add("Could not add solution '" + solutionPath + "' (" + ex.Message + ")");
                }
            }

            HandleExceptions();

            //ensure that all solutions are uploaded in farm
            foreach (string solutionName in solutionNames)
            {
                SPSolution solution = SPFarm.Local.Solutions[solutionName];
                if (solution == null)
                {
                    exceptions.Add("Solution '" + solutionName + "' not found in solution store");
                }                
            }

            HandleExceptions();

            LogMessage(FormatHeader("Adding solutions finished successfully"), LogLevel.High);
            LogMessage("", LogLevel.High);
            LogMessage(FormatHeader("Deploying solutions"), LogLevel.High);

            Collection<SPSolution> uploadedSPSolutions = GetSPSolutions(solutionNames);            
            foreach (SPSolution solution in uploadedSPSolutions)
            {
              string solutionName = solution.Name;

                //run this section 3 times if a failure occurs
                bool success = false;
                int runCount = 0;
                while(runCount < this.ParsedRetries)
                {
                    runCount++;
                    if (solution.ContainsWebApplicationResource)
                    {
                        if ((webApplicationNames.Length == 0) || this.AllContentUrls)
                        {
                            //no webapps given, deploy to all content urls
                            LogMessage("Deploying '" + solution.Name + "' to all content urls...", LogLevel.Normal);
                            Collection<SPWebApplication> contentApplications = GetAllContentWebApps();
                            solution.Deploy(DateTime.Now - TimeSpan.FromDays(1), AllowGACDeployment, contentApplications, this.Force);                        
                        }
                        else
                        {
                            //several webapps given, deploy to them
                            LogMessage("Deploying '" + solution.Name + "' to " + this.WebApplications + "...", LogLevel.Normal);
                            solution.Deploy(DateTime.Now - TimeSpan.FromDays(1), AllowGACDeployment, GetWebApplicationsFromNames(webApplicationNames), this.Force);
                         }
                    }
                    else
                    {
                        LogMessage("Deploying '" + solution.Name + "' globally...", LogLevel.Normal);
                        solution.Deploy(DateTime.Now - TimeSpan.FromDays(1), true, true);                    
                    }

                    ExecAdmSvcJobs(false, false);

                    WaitForFinishedDeployment(solutionName);

                    if (SolutionsSuccessfullyDeployed(solutionName))
                    {
                        runCount = this.ParsedRetries + 1;
                        success = true;
                    }
                    else
                    {
                        LogWarning("Deployment of solution " + solution.Name + " failed with " + solution.LastOperationDetails + ". Retry.");
                    }
                    if (!success)
                    {
                        exceptions.Add("Could not deploy solution " + solution.Name + " (" + solution.LastOperationDetails + ")");
                    }
                }                
            }

            LogMessage(FormatHeader("Deploying solutions finished successfully"), LogLevel.High);
            LogMessage("", LogLevel.High); 
        }

        private bool SolutionsSuccessfullyDeployed(string solutionName)
        {
          SPSolution solution = GetSPSolution(solutionName);
          if (solution != null)
          {
            if (solution.LastOperationResult == SPSolutionOperationResult.DeploymentSucceeded)
            {
              return true;
            }
            if (solution.Deployed)
            {
              return true;
            }
            return false;
          }
          else
          {
            LogMessage("SolutionsSuccessfullyDeployed: Solution " + solutionName + " not found", LogLevel.Low);
          }
          return true; //if solution not there all is ok
        }
                
        public override void ValidateSystem()
        {
            // TODO: Check that all solutions are deployed
        }
    }
}
